package simulator;

public class UBJCache extends BufferCache {
	
	int journalLimit;
	int checkptLimit;
	int checkptTimer;
	int numCopies;
	
	public UBJCache(int numFrames)
	{
		super(numFrames);
		this.ja = null;
		journalLimit = (int) numFrames / 4;
		checkptLimit = 0;
		checkptTimer = 0;
		numCopies = 0;
	}
	
	public void updateBlock(String fileName, int data)
	{
		//lazyCommit();
		int frameNo = findBlock(fileName);
		
		if(frameNo >= 0){
			hits++;
		}
		
		else{
		//Cache miss. For now, let's bring it into the buffer cache
			misses++;
			frameNo = readIn(fileName); //read in the data from the specified file ot the frame.
		//this is the index of our new frame of data
		}
		
		Frame f = frames[frameNo];
		
		if(!f.isFrozen){
			f.setData(data);
			f.timeStamp = ss.getTime();
		}
		
		else{
		f.isUptodate = false;
		makeCopy(f.block, data, frameNo);}
	}
	
	public int makeCopy(Block b, int newData, int prevFrame)
	{
		numCopies++;
		//Find new place to place block
		int lru = LRU();
		Frame f = frames[lru];
		if(f.block != null){ //frame f has a block currently
			evict(f);}
		
		Block newBlock = new Block(b.getName(), newData);
		f.block = newBlock;
		f.timeStamp = ss.getTime();
		f.isDirty = true;
		f.isFrozen = false;
		f.isUptodate = true;
		f.prevFrame = prevFrame;
		
		return 0;
	}
	
	
	/**
	 * Victim of UBJ replacement
	 * NOT USED CURRENTLY
	 * @return
	 */
	protected int UBJ_victim()
	{
		/*
		
		for(int i = 1; i < frames.length; i++)
		{//compare earliest timestamped frame against every other frame
			Frame currentFrame = frames[i];
			boolean isCandidate = ((currentFrame.isFrozen && !currentFrame.isUptodate) || !currentFrame.isDirty);
			if(isCandidate)
				return i;
		}*/
		
		int frameNo = 0;
		for(int i = 1; i < frames.length; i++)
		{//compare earliest timestamped frame against every other frame
			Frame currentFrame = frames[i];
			if(currentFrame.timeStamp < frames[frameNo].timeStamp && currentFrame.isFrozen==false)
			{//we have a new earliest frame
				frameNo = i;}
		}
		return frameNo;
	}
	
	/**
	 * Remove the block data from frame f
	 * NOT USED CURRENTLY
	 * @param f
	 */
	protected void evictUBJ(Frame f)
	{
		Block b = f.block;
		if(b != null){
			
			if(f.isDirty)
				fs.updateFile(b.getName(), b.getData());
			f.block = null; //free the frame so another block can be put in it
		}
		
		f.resetFlags();
	}
	
	public void commitAll()
	{
		for(int i=0; i<frames.length; i++){
			Frame f = frames[i];
			if(f.getIsDirty() && f.isFrozen == false){
				f.isFrozen = true;
				if(f.prevFrame >= 0)
				{//release frame
					int frameNo = f.prevFrame;
					f.prevFrame = -1; //now 'f' is the only frozen block for this file, don't point to anything else
					Frame f2 = frames[frameNo];
					 frames[frameNo] = new Frame();
				}
			}
		}
		
		if(NumFrozen() >= journalLimit){
			checkpointAll();
			checkptLimit++;}
	}
	
	public int NumFrozen()
	{
		
		int numFrozen = 0;
		for(int i = 0; i < frames.length; i++)
		{
			if(frames[i].isFrozen && frames[i].block != null)
				numFrozen++;
		}
		
		return numFrozen;
	}
	
	
	/***
	 * Perform checkpoint operation. All frozen blocks get their changes updated in the file system
	 */
	public void checkpointAll()
	{
		for(int i=0; i<frames.length; i++){
			if(frames[i].isFrozen == true){
				
				if(frames[i].isUptodate){
					Block b = frames[i].block;
					fs.updateFile(b.getName(), b.getData());
					frames[i].isFrozen = false;
					frames[i].isDirty = false;
				}
				else{
					frames[i] = new Frame();
					removeRef(i);}
				
			}
		}
	}
	
	/**
	 * Remove reference to a frame that has just been freed
	 * @param index
	 */
	public void removeRef(int index)
	{
		for(int i = 0; i < frames.length; i++)
		{
			Frame f = frames[i];
			if(f.prevFrame == index)
				f.prevFrame = -1; // no need to point to previous block
		}
	}

	@Override
	public void evictDirty(Block b) {
		// TODO Auto-generated method stub
		fs.updateFile(b.getName(), b.getData());
	}

	@Override
	public void flushDirty() {
		// TODO Auto-generated method stub
		commitAll();
		checkpointAll();
	}
	
	public void UpdateCheckPtTimer(){
		checkptTimer++;
	}
	
	public int GetCheckPtTimer(){
		return checkptTimer;}
	
	public int GetCheckPtLimit(){
		return checkptLimit;}
	
	@Override
	public String SimString() {
		// TODO Auto-generated method stub
		String str = "Sim type: UBJ\nHits: " + hits + "\nMisses: " + misses
				+ "\nCheckpoints triggered by timer: " + checkptTimer
				+ "\nCheckpoints triggered by space limit reached: " + checkptLimit
				+ "\nTotal number of copied blocks during sim: " + numCopies;
		return str;
	}
	
	
}
